#include "stringutil.h"
#include <algorithm>
#include <sstream>

#if defined(__WIN32__) || defined(_WIN32)
#   include <io.h>
#   include <windows.h>
#   include <string>
#endif

ZH_NAMESPACE_BEGIN

	using std::stringstream;
	//-----------------------------------------------------------------------
	const String StringUtil::BLANK;
	//-----------------------------------------------------------------------
	void StringUtil::trim(String& str, bool left, bool right)
	{
		/*
		size_t lspaces, rspaces, len = length(), i;

		lspaces = rspaces = 0;

		if( left )
		{
			// Find spaces / tabs on the left
			for( i = 0;
				i < len && ( at(i) == ' ' || at(i) == '\t' || at(i) == '\r');
				++lspaces, ++i );
		}
    
		if( right && lspaces < len )
		{
			// Find spaces / tabs on the right
			for( i = len - 1;
				i >= 0 && ( at(i) == ' ' || at(i) == '\t' || at(i) == '\r');
				rspaces++, i-- );
		}

		*this = substr(lspaces, len-lspaces-rspaces);
		*/
		static const String delims = " \t\r";
		if(right)
			str.erase(str.find_last_not_of(delims)+1); // trim right
		if(left)
			str.erase(0, str.find_first_not_of(delims)); // trim left
	}

	//-----------------------------------------------------------------------
	StringVector StringUtil::split( const String& str, const String& delims, unsigned int maxSplits)
	{
		StringVector ret;
		// Pre-allocate some space for performance
		ret.reserve(maxSplits ? maxSplits+1 : 10);    // 10 is guessed capacity for most case

		unsigned int numSplits = 0;

		// Use STL methods 
		size_t start, pos;
		start = 0;
		do 
		{
			pos = str.find_first_of(delims, start);
			if (pos == start)
			{
				// Do nothing
				start = pos + 1;
			}
			else if (pos == String::npos || (maxSplits && numSplits == maxSplits))
			{
				// Copy the rest of the string
				ret.push_back( str.substr(start) );
				break;
			}
			else
			{
				// Copy up to delimiter
				ret.push_back( str.substr(start, pos - start) );
				start = pos + 1;
			}
			// parse up to next real data
			start = str.find_first_not_of(delims, start);
			++numSplits;

		} while (pos != String::npos);



		return ret;
	}
	//-----------------------------------------------------------------------
	StringVector StringUtil::tokenise( const String& str, const String& singleDelims, const String& doubleDelims, unsigned int maxSplits)
	{
		StringVector ret;
		// Pre-allocate some space for performance
		ret.reserve(maxSplits ? maxSplits+1 : 10);    // 10 is guessed capacity for most case

		unsigned int numSplits = 0;
		String delims = singleDelims + doubleDelims;

		// Use STL methods 
		size_t start, pos;
		char curDoubleDelim = 0;
		start = 0;
		do 
		{
			if (curDoubleDelim != 0)
			{
				pos = str.find(curDoubleDelim, start);
			}
			else
			{
				pos = str.find_first_of(delims, start);
			}

			if (pos == start)
			{
				char curDelim = str.at(pos);
				if (doubleDelims.find_first_of(curDelim) != String::npos)
				{
					curDoubleDelim = curDelim;
				}
				// Do nothing
				start = pos + 1;
			}
			else if (pos == String::npos || (maxSplits && numSplits == maxSplits))
			{
				if (curDoubleDelim != 0)
				{
					//Missing closer. Warn or throw exception?
				}
				// Copy the rest of the string
				ret.push_back( str.substr(start) );
				break;
			}
			else
			{
				if (curDoubleDelim != 0)
				{
					curDoubleDelim = 0;
				}

				// Copy up to delimiter
				ret.push_back( str.substr(start, pos - start) );
				start = pos + 1;
			}
			if (curDoubleDelim == 0)
			{
				// parse up to next real data
				start = str.find_first_not_of(singleDelims, start);
			}
        
			++numSplits;

		} while (pos != String::npos);

		return ret;
	}
	//-----------------------------------------------------------------------
	void StringUtil::toLowerCase(String& str)
	{
		std::transform(
			str.begin(),
			str.end(),
			str.begin(),
			tolower);
	}

	//-----------------------------------------------------------------------
	void StringUtil::toUpperCase(String& str) 
	{
		std::transform(
			str.begin(),
			str.end(),
			str.begin(),
			toupper);
	}
	//-----------------------------------------------------------------------
	bool StringUtil::startsWith(const String& str, const String& pattern, bool lowerCase)
	{
		size_t thisLen = str.length();
		size_t patternLen = pattern.length();
		if (thisLen < patternLen || patternLen == 0)
			return false;

		String startOfThis = str.substr(0, patternLen);
		if (lowerCase)
			StringUtil::toLowerCase(startOfThis);

		return (startOfThis == pattern);
	}
	//-----------------------------------------------------------------------
	bool StringUtil::endsWith(const String& str, const String& pattern, bool lowerCase)
	{
		size_t thisLen = str.length();
		size_t patternLen = pattern.length();
		if (thisLen < patternLen || patternLen == 0)
			return false;

		String endOfThis = str.substr(thisLen - patternLen, patternLen);
		if (lowerCase)
			StringUtil::toLowerCase(endOfThis);

		return (endOfThis == pattern);
	}
	//-----------------------------------------------------------------------
	String StringUtil::standardisePath(const String& init)
	{
		String path = init;

		std::replace( path.begin(), path.end(), '\\', '/' );
		if( path[path.length() - 1] != '/' )
			path += '/';

		return path;
	}
	//-----------------------------------------------------------------------
	void StringUtil::splitFilename(const String& qualifiedName, 
		String& outBasename, String& outPath)
	{
		String path = qualifiedName;
		// Replace \ with / first
		std::replace( path.begin(), path.end(), '\\', '/' );
		// split based on final /
		size_t i = path.find_last_of('/');

		if (i == String::npos)
		{
			outPath.clear();
			outBasename = qualifiedName;
		}
		else
		{
			outBasename = path.substr(i+1, path.size() - i - 1);
			outPath = path.substr(0, i+1);
		}

	}
	//-----------------------------------------------------------------------
	void StringUtil::splitBaseFilename(const String& fullName, 
		String& outBasename, String& outExtention)
	{
		size_t i = fullName.find_last_of(".");
		if (i == String::npos)
		{
			outExtention.clear();
			outBasename = fullName;
		}
		else
		{
			outExtention = fullName.substr(i+1);
			outBasename = fullName.substr(0, i);
		}
	}
	// ----------------------------------------------------------------------------------------------------------------------------------------------
	void StringUtil::splitFullFilename(	const String& qualifiedName, 
		String& outBasename, String& outExtention, String& outPath )
	{
		String fullName;
		splitFilename( qualifiedName, fullName, outPath );
		splitBaseFilename( fullName, outBasename, outExtention );
	}
	//-----------------------------------------------------------------------
	bool StringUtil::match(const String& str, const String& pattern, bool caseSensitive)
	{
		String tmpStr = str;
		String tmpPattern = pattern;
		if (!caseSensitive)
		{
			StringUtil::toLowerCase(tmpStr);
			StringUtil::toLowerCase(tmpPattern);
		}

		String::const_iterator strIt = tmpStr.begin();
		String::const_iterator patIt = tmpPattern.begin();
		String::const_iterator lastWildCardIt = tmpPattern.end();
		while (strIt != tmpStr.end() && patIt != tmpPattern.end())
		{
			if (*patIt == '*')
			{
				lastWildCardIt = patIt;
				// Skip over looking for next character
				++patIt;
				if (patIt == tmpPattern.end())
				{
					// Skip right to the end since * matches the entire rest of the string
					strIt = tmpStr.end();
				}
				else
				{
					// scan until we find next pattern character
					while(strIt != tmpStr.end() && *strIt != *patIt)
						++strIt;
				}
			}
			else
			{
				if (*patIt != *strIt)
				{
					if (lastWildCardIt != tmpPattern.end())
					{
						// The last wildcard can match this incorrect sequence
						// rewind pattern to wildcard and keep searching
						patIt = lastWildCardIt;
						lastWildCardIt = tmpPattern.end();
					}
					else
					{
						// no wildwards left
						return false;
					}
				}
				else
				{
					++patIt;
					++strIt;
				}
			}

		}
		// If we reached the end of both the pattern and the string, we succeeded
		if (patIt == tmpPattern.end() && strIt == tmpStr.end())
		{
    		return true;
		}
		else
		{
			return false;
		}

	}
	//-----------------------------------------------------------------------
	const String StringUtil::replaceAll(const String& source, const String& replaceWhat, const String& replaceWithWhat)
	{
		String result = source;
		String::size_type pos = 0;
		while(1)
		{
			pos = result.find(replaceWhat,pos);
			if (pos == String::npos) break;
			result.replace(pos,replaceWhat.size(),replaceWithWhat);
			pos += replaceWithWhat.size();
		}
		return result;
	}

	//-----------------------------------------------------------------------
	std::wstring	StringUtil::trans2Unicode(const String& str)
	{
		const int len = ::MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1,
			0, 0);
		if (0 == len)
			return false;

		std::auto_ptr<wchar_t> buf(new wchar_t[len + 1]);

		::MultiByteToWideChar(CP_ACP, 0, str.c_str(), str.length() + 1,
			buf.get(), len);
		buf.get()[len] = 0;
		
		return std::wstring(buf.get());
	}

	String StringUtil::trans2MultiByte(const std::wstring& str)
	{
		const int len = ::WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1,
			0, 0, 0, 0);
		if (0 == len)
			return false;


		std::auto_ptr<char> buf(new char[len + 1]);
		::WideCharToMultiByte(CP_ACP, 0, str.c_str(), -1,
			buf.get(), len, 0, 0);
		buf.get()[len] = 0;

		return String(buf.get());
	}

	//////////////////////////////////////////////////////////////////////////
	// convers
	//-----------------------------------------------------------------------
	String StringUtil::toString(float val, unsigned short precision, 
		unsigned short width, char fill, std::ios::fmtflags flags)
	{
		stringstream stream;
		stream.precision(precision);
		stream.width(width);
		stream.fill(fill);
		if (flags)
			stream.setf(flags);
		stream << val;
		return stream.str();
	}
	//-----------------------------------------------------------------------
	String StringUtil::toString(int val, 
		unsigned short width, char fill, std::ios::fmtflags flags)
	{
		stringstream stream;
		stream.width(width);
		stream.fill(fill);
		if (flags)
			stream.setf(flags);
		stream << val;
		return stream.str();
	}

	String StringUtil::toString(size_t val, 
		unsigned short width, char fill, std::ios::fmtflags flags)
	{
		stringstream stream;
		stream.width(width);
		stream.fill(fill);
		if (flags)
			stream.setf(flags);
		stream << val;
		return stream.str();
	}
	//-----------------------------------------------------------------------
	String StringUtil::toString(unsigned long val, 
		unsigned short width, char fill, std::ios::fmtflags flags)
	{
		stringstream stream;
		stream.width(width);
		stream.fill(fill);
		if (flags)
			stream.setf(flags);
		stream << val;
		return stream.str();
	}
	//-----------------------------------------------------------------------
	String StringUtil::toString(long val, 
		unsigned short width, char fill, std::ios::fmtflags flags)
	{
		stringstream stream;
		stream.width(width);
		stream.fill(fill);
		if (flags)
			stream.setf(flags);
		stream << val;
		return stream.str();
	}

	//-----------------------------------------------------------------------
	String StringUtil::toString(bool val, bool yesNo)
	{
		if (val)
		{
			if (yesNo)
			{
				return "yes";
			}
			else
			{
				return "true";
			}
		}
		else
			if (yesNo)
			{
				return "no";
			}
			else
			{
				return "false";
			}
	}
	//-----------------------------------------------------------------------
	float StringUtil::parseFloat(const String& val, float defaultValue)
	{
		// Use istringstream for direct correspondence with toString
		StringStream str(val);
		float ret = defaultValue;
		str >> ret;

		return ret;
	}
	//-----------------------------------------------------------------------
	int StringUtil::parseInt(const String& val, int defaultValue)
	{
		// Use istringstream for direct correspondence with toString
		StringStream str(val);
		int ret = defaultValue;
		str >> ret;

		return ret;
	}
	//-----------------------------------------------------------------------
	unsigned int StringUtil::parseUnsignedInt(const String& val, unsigned int defaultValue)
	{
		// Use istringstream for direct correspondence with toString
		StringStream str(val);
		unsigned int ret = defaultValue;
		str >> ret;

		return ret;
	}
	//-----------------------------------------------------------------------
	long StringUtil::parseLong(const String& val, long defaultValue)
	{
		// Use istringstream for direct correspondence with toString
		StringStream str(val);
		long ret = defaultValue;
		str >> ret;

		return ret;
	}
	//-----------------------------------------------------------------------
	unsigned long StringUtil::parseUnsignedLong(const String& val, unsigned long defaultValue)
	{
		// Use istringstream for direct correspondence with toString
		StringStream str(val);
		unsigned long ret = defaultValue;
		str >> ret;

		return ret;
	}
	//-----------------------------------------------------------------------
	bool StringUtil::parseBool(const String& val, bool defaultValue)
	{
		if ((StringUtil::startsWith(val, "true") || StringUtil::startsWith(val, "yes")
			|| StringUtil::startsWith(val, "1")))
			return true;
		else if ((StringUtil::startsWith(val, "false") || StringUtil::startsWith(val, "no")
			|| StringUtil::startsWith(val, "0")))
			return false;
		else
			return defaultValue;
	}

	//-----------------------------------------------------------------------
	bool StringUtil::isNumber(const String& val)
	{
		StringStream str(val);
		float tst;
		str >> tst;
		return !str.fail() && str.eof();
	}

ZH_NAMESPACE_END
